package zpe.controledeacesso.jsf.view.bean;

import static zpe.genericcrud.jsf.view.bean.FacesUtils.addErrorMessage;
import static zpe.genericcrud.jsf.view.bean.FacesUtils.addInfoMessage;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.servlet.http.HttpServletRequest;

import org.primefaces.model.DualListModel;
import org.springframework.context.ApplicationContext;
import org.springframework.web.jsf.FacesContextUtils;

import zpe.controledeacesso.model.bean.GrupoDeUsuario;
import zpe.controledeacesso.model.bean.NivelAcesso;
import zpe.controledeacesso.model.bean.UnidadeTrabalho;
import zpe.controledeacesso.model.bean.Usuario;
import zpe.controledeacesso.security.Acesso;
import zpe.controledeacesso.service.UsuarioService;
import zpe.controledeacesso.util.EmailUtil;
import zpe.controledeacesso.util.StringUtils;
import zpe.controledeacesso.util.UsuarioUtil;
import zpe.genericcrud.exception.ApplicationException;
import zpe.genericcrud.jsf.view.bean.AbstractCrudBean;
import zpe.genericcrud.jsf.view.bean.FacesUtils;

@SuppressWarnings("unchecked")

@ManagedBean(name = "usuarioBean")
@ViewScoped
public class UsuarioBean extends AbstractCrudBean<Usuario, UsuarioService>
		implements Serializable {

	private static final long serialVersionUID = 533658662263452568L;

	@ManagedProperty("#{usuarioService}")
	protected UsuarioService service;
	
	private UIInput input;

	private List<GrupoDeUsuario> grupos = new ArrayList<GrupoDeUsuario>();
	private String novaSenha;
	private String novaSenhaRepita;
	private String senhaAtual;
	private String email;

	private DualListModel<GrupoDeUsuario> listaGrupos;

	@Override
	public void setService(UsuarioService service) {
		super.setService(service);
	}
	
	public UIInput getInput() {
		return input;
	}

	public void setInput(UIInput input) {
		this.input = input;
	}

	@Override
	public void update(ActionEvent event) {
		try {
			
			validar();
			
			if(FacesUtils.isMessageErro()) {
				return;
			}
				
			getEntity().setLogin(getEntity().getLogin().trim());
			getEntity().setEmail(getEntity().getEmail().trim());
			getService().validaEmail(getEntity(), true);
			getService().validaCpf(getEntity(), true);
			getService().validaLogin(getEntity(), true);
			
			for (GrupoDeUsuario grupoDeUsuario : getListaGrupos() .getTarget()) {
				if(!getEntity().getGrupos().contains(grupoDeUsuario)) {  
				  getEntity().getGrupos().add(grupoDeUsuario);
				}
			}
			
			getEntity().getGrupos().removeAll(getListaGrupos().getSource());
			
			super.update(event);
				
		} catch (ApplicationException e) {
			addErrorMessage("Erro: " + e.getMessage());
		} catch (Exception e) {
			addErrorMessage(("Erro: Ocorreu um problema: " + e.getMessage()));
		}
	}

	public static int getIdade(Date data) {

		Calendar hoje = Calendar.getInstance();

		Date temp = new Date(hoje.getTimeInMillis() - data.getTime());

		Calendar c = Calendar.getInstance();

		c.setTime(temp);

		int idade = c.get(Calendar.YEAR) - 1970;
		
		return idade;
	}
	
	private void validar() {
		
		if (getEntity().getUnidadeDeTrabalho() == null || getEntity().getUnidadeDeTrabalho().getId() == null)
			addErrorMessage("Informe a unidade de trabalho.");
		
		List<GrupoDeUsuario> target = getListaGrupos().getTarget();
		if( target == null || target.isEmpty() ) {
			addErrorMessage("É obrigatório informar pelo menos um grupo de usuários.");
		}
	}
	
	@Override
	public void save(ActionEvent event) {
		
		getEntity().setGrupos(new ArrayList<GrupoDeUsuario>());

		try {
			
			validar();
			
			if(FacesUtils.isMessageErro()) {
				return;
			}
			
			for (GrupoDeUsuario grupoDeUsuario : getListaGrupos()
					.getTarget()) {
				getEntity().getGrupos().add(grupoDeUsuario);
			}
			
			String senha = EmailUtil.geraSenha();
			
			getEntity().setSenha(senha);
			
			getService().insert(getEntity());
			
			addInfoMessage("Usuário salvo com sucesso.");
			
			String email = getEntity().getEmail(),
				   login = getEntity().getLogin();
			
			try {
				
				getService().enviarEmail(email, login, senha);
			} catch (Exception e) {
				
				addErrorMessage("Falha no envio do email.");
				
			   try {
				
				    getService().atualizarSenhaToLogin(getEntity());
				    
			   	} catch (Exception e2) {
			   	}	
			}
			
			setCurrentState(STATE_INSERT);
			
			setEntity(getNewEntityInstance());
		} catch (ApplicationException e) {
			addErrorMessage(e.getMessage());
		} catch (Exception e) {
			addErrorMessage(getMessage("ERROR_INSERT"));
		}
	}

	@Override
	public void find(ActionEvent event) {

		if (getEntitySearch().getDescricao() == null
				&& getEntitySearch().getLogin() == null
				&& getEntitySearch().getEmail() == null) {
			addErrorMessage("Por favor, preencha algum campo para poder fazer a consulta.");
		} else {
			super.find(event);
			if (getResultSearch().getWrappedData() != null) {
				List<Usuario> usuarios = (List<Usuario>) getResultSearch()
						.getWrappedData();
				Collections.sort(usuarios, new Comparator<Usuario>() {
					@Override
					public int compare(Usuario o1, Usuario o2) {
						return o1.getDescricao().compareTo(o2.getDescricao());
					}
				});
			}
		}

	}

	@Override
	public void prepareUpdate(ActionEvent event) {
		super.prepareUpdate(event);
		setGrupos(new ArrayList<GrupoDeUsuario>());
		Usuario usuario = UsuarioUtil.getUsuarioLogado();

		setEntity(getService().findByPrimaryKey(Usuario.class,
				getEntity().getId()));
		int modulo = Acesso.getModuloSistema();
		NivelAcesso maiorNivel = usuario.getMaiorNivel(modulo);

		List<GrupoDeUsuario> grupos = getService().getServiceGrupoDeUsuarios()
				.findByNivel(maiorNivel, modulo);
		
		GrupoDeUsuario grupo = null;
		List<GrupoDeUsuario> groups = new ArrayList<GrupoDeUsuario>(0);
		for(int i = 0; i < getEntity().getGrupos().size(); i++) {
			grupo = getEntity().getGrupos().get(i);
			
			if( grupo.getModulo().getId().intValue() == (modulo) ) {
				groups.add(grupo);
			}
		}
		
		grupos.removeAll(groups);

		listaGrupos = new DualListModel<GrupoDeUsuario>(grupos, groups);
		
		/*
		 * Pega os grupos do usuário a ser alterado que podem ser vistos pelo
		 * usuário logado
		 */
		for (GrupoDeUsuario e : getEntity().getGrupos()) {
			if (grupos.contains(e))
				getGrupos().add(e);
			else
				// Os que não podem ser vistos pelo usuário logado ficam numa
				// lista auxiliar para serem preservados.
				getEntity().getGruposAux().add(e);
		}
		setCurrentState(STATE_EDIT);
	}

	@Override
	public void prepareInsert(ActionEvent event) {
		
		super.prepareInsert(event);
		
		setGrupos(new ArrayList<GrupoDeUsuario>());
		int modulo = Acesso.getModuloSistema();

		Map<String, Object> map = new HashMap<String, Object>();
		map.put("modulo.id", modulo);

		grupos = getService().getServiceGrupoDeUsuarios().findByFilter(
				GrupoDeUsuario.class, map);
		listaGrupos = new DualListModel<GrupoDeUsuario>(grupos, getEntity().getGrupos());
	}

	public static ApplicationContext getApplicationContext() {
		return FacesContextUtils.getWebApplicationContext(FacesContext
				.getCurrentInstance());
	}

	public boolean getPrimeiroAcesso() {
		setEntity(UsuarioUtil.getUsuarioLogado());
		if (getEntity().getAtualizouSenha() != null) {
			if (getEntity().getAtualizouSenha().toUpperCase().equals("S"))
				return false;
		}
		setEntity(new Usuario());
		return true;
	}

	/**
	 * Método usado no primeiro acesso para atualizar senha.
	 * 
	 * @param event
	 */
	@Deprecated
	public void atualizaSenha(ActionEvent event) {
		try {
			if (!novaSenha.toUpperCase().equals(novaSenhaRepita.toUpperCase())) {
				FacesUtils
						.addErrorMessage("A nova senha não confere, tente novamente!");
			} else {
				setEntity(UsuarioUtil.getUsuarioLogado());
				getEntity().setSenha(StringUtils.md5(novaSenha));
				getEntity().setAtualizouSenha(new String("S"));
				getService().update(getEntity());
				FacesUtils.addInfoMessage("Senha alterada com sucesso!");
			}
			setEntity(new Usuario());
		} catch (Exception e) {
			FacesUtils
					.addErrorMessage("Ocorreu um erro durante a atualização da senha do usuário.");
		}
	}

	public void alteraSenha(ActionEvent ev) {
		try {

			setEntity(UsuarioUtil.getUsuarioLogado());

			if (!StringUtils.md5(senhaAtual.trim().toUpperCase()).equals(
					getEntity().getSenha().trim()))
				throw new ApplicationException("A senha atual não confere!");

			if (!novaSenha.toUpperCase().equals(novaSenhaRepita.toUpperCase()))
				throw new ApplicationException(
						"A nova senha não confere, tente novamente!");

			// chamada do DAO
			getEntity().setSenha(StringUtils.md5(novaSenha.toUpperCase()));
			getEntity().setAtualizouSenha("S");
			getService().update(getEntity());
			// UsuarioDao.alteraSenha(u.getLogin(), novaSenha);

			FacesUtils.addInfoMessage("Senha alterada com sucesso!");
		} catch (ApplicationException e) {
			getEntity().setAtualizouSenha("N");
			addErrorMessage(e.getMessage());
		} catch (Exception e) {
			getEntity().setAtualizouSenha("N");
			addErrorMessage("Digite a nova senha.");
		}
		setEntity(new Usuario());
	}

	/**
	 * Pega um objeto que est� na sess�o.
	 * 
	 * @param atributo
	 *            Nome do objeto.
	 * @return Objeto que est� na sess�o.
	 */
	@Deprecated
	public Object pegaAtributoSessao(String atributo) {
		HttpServletRequest request = (HttpServletRequest) FacesContext
				.getCurrentInstance().getExternalContext().getRequest();
		return request.getSession().getAttribute(atributo);
	}

	public void recuperarSenha(ActionEvent e) {
		try {
			getService().reiniciarSenha(getEntity());
			FacesUtils
					.addInfoMessage("Em instantes você receberá um email com uma senha temporária e com informações para alteração da senha.");
			setEntity(new Usuario());
		} catch (ApplicationException ex) {
			addErrorMessage(ex.getMessage());
		} catch (Exception ex) {
			addErrorMessage("Erro: Ocorreu um problema durante a recuperação da senha. Tente novamente mais tarde ou contate o administrador do sistema. ");
			ex.printStackTrace();
		}
	}

	/**
	 * @return the novaSenha
	 */
	public String getNovaSenha() {
		return novaSenha;
	}

	/**
	 * @param novaSenha
	 *            the novaSenha to set
	 */
	public void setNovaSenha(String novaSenha) {
		this.novaSenha = novaSenha;
	}

	/**
	 * @return the novaSenhaRepita
	 */
	public String getNovaSenhaRepita() {
		return novaSenhaRepita;
	}

	/**
	 * @param novaSenhaRepita
	 *            the novaSenhaRepita to set
	 */
	public void setNovaSenhaRepita(String novaSenhaRepita) {
		this.novaSenhaRepita = novaSenhaRepita;
	}

	/**
	 * @return the senhaAtual
	 */
	public String getSenhaAtual() {
		return senhaAtual;
	}

	/**
	 * @param senhaAtual
	 *            the senhaAtual to set
	 */
	public void setSenhaAtual(String senhaAtual) {
		this.senhaAtual = senhaAtual;
	}

	/**
	 * @return the email
	 */
	public String getEmail() {
		return email;
	}

	/**
	 * @param email
	 *            the email to set
	 */
	public void setEmail(String email) {
		this.email = email;
	}

	public List<GrupoDeUsuario> getGrupos() {
		return grupos;
	}

	public void setGrupos(List<GrupoDeUsuario> grupos) {
		this.grupos = grupos;
	}

	/**********
	 * 
	 * Unidades de Trabalho
	 * 
	 */
	@SuppressWarnings("unused")
	@Deprecated
	private String numeroUnidade;// Está fora do método para verifica se a
									// consulta já foi efetuada.


	public void selecionarUnidade(ActionEvent event) {
		getEntity().setUnidadeDeTrabalho(
				(UnidadeTrabalho) getUnidades().getRowData());
	}

	/**
	 * Remove todas as entidades que estão lista entities e atualiza a lista de
	 * resultados.
	 * 
	 * @param event
	 */
	public void deleteAll(ActionEvent event) {
		try {
			if (getEntities().getRowCount() > 0) {
				getService().removeAll(
						(List<Usuario>) getEntities().getWrappedData(),
						UsuarioUtil.getUsuarioLogado());

				// Remove os dados das lista de consultas.
				getOriginalResult().removeAll(
						(List<Usuario>) getEntities().getWrappedData());
				((List<Usuario>) getResultSearch().getWrappedData())
						.removeAll((List<Usuario>) getEntities()
								.getWrappedData());

				addInfoMessage(getMessage("SUCCESS_ITENS_REMOVE"));
				setCurrentState(STATE_SEARCH);
				getEntities().setWrappedData(null);
			}
		} catch (ApplicationException e) {
			addErrorMessage(e.getMessage());
		} catch (Exception e) {
			addErrorMessage(getMessage("ERROR_MESSAGE"));
		}
	}

	public void setListaGrupos(DualListModel<GrupoDeUsuario> listaGrupos) {
		this.listaGrupos = listaGrupos;
	}

	public DualListModel<GrupoDeUsuario> getListaGrupos() {
		return listaGrupos;
	}
	
	
	/** Manipulação da LOV de Unidade de Trabalho  */
	
	private DataModel<UnidadeTrabalho> unidades = new ListDataModel<UnidadeTrabalho>( new ArrayList<UnidadeTrabalho>(0) );

	public DataModel<UnidadeTrabalho> getUnidades() {
		return unidades;
	}

	public void setUnidades(DataModel<UnidadeTrabalho> unidades) {
		this.unidades = unidades;
	}
	
	public void findUnidadeByCodigo(AjaxBehaviorEvent event) {
		try {
			
			((List<UnidadeTrabalho>) getUnidades().getWrappedData()).clear();
			
			UnidadeTrabalho unidade = getEntity().getUnidadeDeTrabalho();
			
			
			Usuario usuario = UsuarioUtil.getUsuarioLogado();
			unidade.setDescricao(null);
			
			List<UnidadeTrabalho> unidades = getService().getServiceUnidadeDeTrabalho().consultaUnidadesByNivelAcesso(usuario, unidade);
			
			if(!unidades.isEmpty()) {
				
				if( unidades.size() == 1 ) {
					getEntity().setUnidadeDeTrabalho(unidades.get(0));
				} else {
					getUnidades().setWrappedData(unidades);
				}
			}
			
		} catch (ApplicationException ex) {
			FacesUtils.addWarnMessage(ex.getMessage());
			getEntity().setUnidadeDeTrabalho(new UnidadeTrabalho());
		} catch (Exception ex) {
			addErrorMessage("Ocorreu um erro ao consulta a unidade de trabalho.");
		}
	}
	
	public void findUnidadeByDescricao(ActionEvent event) {
		try {
			
			((List<UnidadeTrabalho>) getUnidades().getWrappedData()).clear();
			
			UnidadeTrabalho unidade = getEntity().getUnidadeDeTrabalho();
			String descricao = unidade.getDescricao();
			
			if(org.apache.commons.lang.StringUtils.isBlank(descricao)) {
				getEntity().setUnidadeDeTrabalho( new UnidadeTrabalho() );
				return;
			}
			
			Usuario usuario = UsuarioUtil.getUsuarioLogado();
			
			List<UnidadeTrabalho> unidades = getService().getServiceUnidadeDeTrabalho().consultaUnidadesByNivelAcesso(usuario, unidade);
			
			if(!unidades.isEmpty()) {
				
				if( unidades.size() == 1 ) {
					getEntity().setUnidadeDeTrabalho(unidades.get(0));
				} else {
					getUnidades().setWrappedData(unidades);
				}
			}
			
		} catch (ApplicationException ex) {
			getEntity().setUnidadeDeTrabalho(new UnidadeTrabalho());
			FacesUtils.addWarnMessage(ex.getMessage());
		} catch (Exception ex) {
			addErrorMessage("Ocorreu um erro ao consulta a unidade de trabalho.");
		}
	}
	
	public void selecionarItem(ActionEvent e) {
		
		UnidadeTrabalho unidade = getUnidades().getRowData();
		getEntity().setUnidadeDeTrabalho(unidade);
		((List<UnidadeTrabalho>) getUnidades().getWrappedData()).clear();
	}
	
	public static void main(String[] args) {
		String numero = null;
		
		System.out.println( org.apache.commons.lang.StringUtils.isNotBlank(numero) );
	}
}
